package com.bigbird.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.Set;

public class NioServerV3 {
    public static void main(String[] args) throws IOException {
        try(ServerSocketChannel serverSocketChannel = ServerSocketChannel.open()){
            serverSocketChannel.bind(new InetSocketAddress(1881));
            Selector selector = Selector.open();
            Runnable runnable=()-> {
                try {
                    doDeal(serverSocketChannel, selector);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            };
            new Thread(runnable).start();
            while(true){
                try {
                    SocketChannel accept = serverSocketChannel.accept();
                    if (null != accept) {
                        accept.configureBlocking(false);
                        //订阅该通道的读写时间 当该通道有读写事件时 将获取到此事件进行数据读写
                        accept.register(selector, SelectionKey.OP_READ);
                        selector.wakeup();
                    }
                }catch (IOException exception){
                    throw new RuntimeException(exception);
                }
            }
        }
    }

    private static void doDeal(ServerSocketChannel serverSocketChannel, Selector selector) throws IOException {
        while(true) {
            selector.select();
            Set<SelectionKey> keys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = keys.iterator();
            while (iterator.hasNext()) {
                SelectionKey selectionKey = iterator.next();
                //清楚已处理的
                keys.remove(selectionKey);
                if (selectionKey.interestOps() == SelectionKey.OP_READ) {
                    SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
                    try {
                        if(selectionKey.isValid()&&socketChannel.isConnected()) {
                            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                            int ret;
                            int readbytes=0;
                            while((ret=socketChannel.read(byteBuffer))>0){
                                readbytes+=ret;
                                if(!byteBuffer.hasRemaining()){
                                    break;
                                }
                            }
                            if(readbytes>0){
                                byteBuffer.flip();
                                byte[] bytes = new byte[readbytes];
                                byteBuffer.get(bytes);
                                String string = new String(bytes);
                                System.out.println("socket:"+socketChannel+"  \r\n message:"+ string);
                                ByteBuffer byteBufferWrite = ByteBuffer.wrap("good job".getBytes(StandardCharsets.UTF_8));
                                socketChannel.write(byteBufferWrite);
                                byteBuffer.rewind();
                            }else if(readbytes<0){
                                socketChannel.socket().close();
                                socketChannel.close();
                                selectionKey.cancel();
                            }
                        }else{
                            selectionKey.cancel();
                        }
                    }catch (Exception exception){
                        exception.printStackTrace();
                        socketChannel.socket().close();
                        socketChannel.close();
                        selectionKey.cancel();
                    }
                }else if(selectionKey.interestOps() == SelectionKey.OP_ACCEPT){
                    SelectableChannel channel = selectionKey.channel();
                    if(channel instanceof ServerSocketChannel){
                        try {
                            SocketChannel accept = serverSocketChannel.accept();
                            if (null != accept) {
                                accept.configureBlocking(false);
                                //订阅该通道的读写时间 当该通道有读写事件时 将获取到此事件进行数据读写
                                accept.register(selector, SelectionKey.OP_READ);
                            }
                        }catch (IOException exception){
                            throw new RuntimeException(exception);
                        }
                    }
                }
                else {
                    SelectableChannel channel = selectionKey.channel();
                        System.out.println(channel);
                        System.out.println(selectionKey.interestOps());
                }

            }
        }
    }
}
